home *** CD-ROM | disk | FTP | other *** search
Text File | 1997-03-07 | 27.8 KB | 854 lines | [TEXT/CWIE] |
- unit AysncDriverSample;
-
- (*
- File: AysncDriverSample.p
-
- Contains: A standard Macintosh device driver.
-
- Written by: Quinn "The Eskimo!"
-
- Copyright: © 1996 by Apple Computer, Inc., all rights reserved.
-
- Change History (most recent first):
-
- You may incorporate this sample code into your applications without
- restriction, though the sample code has been provided "AS IS" and the
- responsibility for its operation is 100% yours. However, what you are
- not permitted to do is to redistribute the source as "DSC Sample Code"
- after having made changes. If you're going to re-distribute the source,
- we require that you make it clear in the source that the code was
- descended from Apple Sample Code, but that you've made changes.
- *)
-
- interface
-
- uses
- Types,
- Devices,
- Files;
-
- (* The 5 standard device drive entry points which are called by
- the assembly code in AsyncDriverMain.
- *)
-
- function DRVROpen(pb: ParmBlkPtr; dctl: DCtlPtr): OSErr;
- function DRVRClose(pb: ParmBlkPtr; dctl: DCtlPtr): OSErr;
- function DRVRPrime(pb: ParmBlkPtr; dctl: DCtlPtr): OSErr;
- function DRVRControl(pb: ParmBlkPtr; dctl: DCtlPtr): OSErr;
- function DRVRStatus(pb: ParmBlkPtr; dctl: DCtlPtr): OSErr;
-
- implementation
-
- uses
- Errors,
- OSUtils,
- Memory,
- TextUtils,
- Resources,
- Events,
- AppleTalk,
- FSM,
- DriverGestalt,
-
- PascalA4,
-
- DiskImageCore,
- AsyncDriverCommon;
-
- (* ***** Standard Driver Declarations ***** *)
-
- {$PUSH}
- {$ALIGN MAC68K}
-
- (* The following are type and constant declarations required to field
- standard control and status calls that block device drivers
- are required to support. The question is "Why aren't these defined
- in a system interface file?" /I don't know!/
-
- These are mostly documented in Technote DV 17:
-
- <http://devworld.apple.com/dev/technotes/dv/dv_17.html>
-
- We switch to 68K alignment when declaring these structures, not because
- we expect this code to be compiled for PPC, but because someone
- might use Metrowerk's "68K 4-byte" alignment when compiling
- 68K code.
- *)
-
- (* Constants for standard control calls. *)
-
- const
- (* Traditional control calls, as defined by Technote DV 17. *)
- kKillIOControlCode = 1;
- kVerifyDiskControlCode = 5;
- kFormatDiskControlCode = 6;
- kEjectDiskControlCode = 7;
- kSetTagBufferControlCode = 8;
- kTrackCacheControlCode = 9;
- kPhysicalIconControlCode = 21;
- kMediaIconControlCode = 22;
- kDriveInfoControlCode = 23;
-
- kTrackDumpControlCode = 18244;
-
- (* Extra control calls defined by "Designing PCI Cards and Drivers for
- Power Macintosh Computers", p114 - 117.
- *)
- kSetStartupDriveControlCode = 44;
- kRegisterPartionControlCode = 50;
- kGetADriveControlCode = 51;
- kProhibitMountingControlCode = 52;
- kSetPowerModeControlCode = 70;
-
- (* Constants for standard status calls. *)
-
- const
- (* Traditional control calls, as defined by Technote DV 17. *)
- kReturnFormatListStatusCode = 6;
- kDriveStatusStatusCode = 8;
-
- (* Extra status calls defined by "Designing PCI Cards and Drivers for
- Power Macintosh Computers", p114 - 117.
- *)
- kGetPartitionStatusStatusCode = 50;
- kGetPartitionInfoStatusCode = 51;
- kGetPowerModeStatusCode = 70;
-
- (* Type needed for kReturnFormatListStatusCode call. *)
-
- type
- FormatDesc =
- record
- capacityInBlocks: longint;
- flagsAndOtherInfo: longint;
- end;
- FormatDescPtr = ^FormatDesc;
-
- (* Type needed for kPhysicalIconControlCode call. *)
-
- type
- IconDescription =
- record
- icon: IconType;
- description: Str255;
- end;
-
- (* This parameter block is a copy of the standard IOParamBlock,
- but with variant to allow access to all of the strange fields
- used by block device drivers.
- *)
-
- type
- ExtendedParamBlock =
- record
- qLink: QElemPtr;
- qType: INTEGER;
- ioTrap: INTEGER;
- ioCmdAddr: Ptr;
- ioCompletion: ProcPtr;
- ioResult: OSErr;
- ioNamePtr: StringPtr;
- ioVRefNum: INTEGER;
- ioCRefNum: integer;
- csCode: integer;
- case integer of
-
- kReturnFormatListStatusCode: (
- formatCount: integer;
- formatPoint: FormatDescPtr;
- );
-
- kDriveStatusStatusCode: (
- statusCurrentTrack: integer;
- statusFlags: signedByte;
- statusDiskInPlace: signedByte;
- statusDriveInstalled: signedByte;
- statusNumberOfSides: signedByte;
- statusDriveQElement: DrvQEl;
- );
-
- end;
- ExtendedParamBlockPtr = ^ExtendedParamBlock;
-
- (* Constants for the "disk in place" field of the drive queue element. *)
-
- const
- kDiskNotPresent = 0;
- kDiskJustInserted = 1;
- kDiskHasBeenRead = 2;
-
- {$ALIGN RESET}
- {$POP}
-
- (* ***** Global Variables **** *)
-
- (* We use global variables to hold the physical and media icons because
- they're global to all the drives that we mount.
- *)
-
- var
- gDriverVersion : NumVersion;
- gPhysicalIconInfo : IconDescription;
- gMediaIcon : IconDescription;
-
- (* ***** Asynchronous Stuff **** *)
-
- function PascalCompletion(xpb : XPPParmBlkPtr; errResult : OSErr) : OSErr;
- (* This routine is called by GenericCompletion in response to a
- completed XPP operation. We do the following operations:
-
- 1. Get the error result out of the XPP paramblock.
- 2. If we get no error, we set ioActCount in the active paramblock
- on the head of our driver queue.
- 3. We also adjust dCtlPosition in our DCE.
- *)
- var
- oldA4 : longint;
- dctl : DCtlPtr;
- pbToComplete : ParmBlkPtr;
- begin
- oldA4 := SetUpA4;
- if errResult = 0 then begin
- errResult := xpb^.cmdResult;
- if errResult = noErr then begin
-
- (* Recover the dctl from the longint immediately in
- front of the XPPParamBlock in the DiskImageRecord.
- *)
- dctl := DCtlPtr( LongintPtr(ord4(xpb) - 4)^ );
-
- (* Get the paramblock to complete from the head of our driver queue. *)
- pbToComplete := ParmBlkPtr(dctl^.dCtlQHdr.qHead);
-
- (* Adjust the ioActCount and dCtlPosition fields in the paramblock we're
- about to complete.
- *)
- pbToComplete^.ioActCount := pbToComplete^.ioReqCount;
- dctl^.dCtlPosition := dctl^.dCtlPosition + pbToComplete^.ioActCount;
- end; (* if *)
- end; (* if *)
- PascalCompletion := errResult;
- oldA4 := SetA4(oldA4);
- end; (* PascalCompletion *)
-
- procedure GenericCompletion;
- (*
- ; GenericCompletion
- ;
- ; A generic completion routine that calls through to the high-level
- ; language completion routine MyPascalCompletion, which should be
- ; declared as:
- ;
- ; extern pascal OSErr PascalCompletion(ParamBlockRec pb,
- ; OSErr errResult);
- ;
- ; MyPascalCompletion should return 1 if there is more work to do,
- ; or return <= 0 if this is the last step of this I/O request and
- ; we should complete it by jumping to IODone.
- ;
- ; Assumed Inputs:
- ; a0.l = pointer to the parameter block that just completed
- ; d0.w = error result for that parameter block
- ; myDCE(a0) = param block extension holds DCE for our driver
- ;
- ; Assumed Outputs:
- ; preserves Pascal registers
- ; ie assumes that our caller doesn't care about D0-D2 and A0-A1
- ; when we return
- *)
- asm;
- const
- JIODone = $08FC;
- begin
- move.l a0,-(sp) (* save a0 around PascalCompletion call *)
-
- clr.w -(sp) (* ; d0:=PascalCompletion(param_block,err); *)
- move.l a0,-(sp) (* ; " *)
- move.w d0,-(sp) (* ; " *)
- jsr PascalCompletion (* ; " *)
- move.w (sp)+,d0 (* ; " *)
-
- movea.l (sp)+,a0 (* ; recover a0 (param block), doesn't set flags *)
-
- bgt.s JustRTS (* ; (d0 > 0) => we have extra work, so just return *)
-
- move.l -4(a0),a1 (* ; put the DCE in a1 for sake of IODone *)
- move.l JIODone,-(sp) (* ; put IODone on the stack and return to it *)
- (* ; IODone will do an RTS itself, which will *)
- (* ; return to our caller *)
- JustRTS:
- rts
- end; (* GenericCompletion *)
-
-
- (* ***** Drive Queue Operations ***** *)
-
- function DriveExists (driveNum: integer): Boolean;
- (* This routine returns true if the given drive number is already
- being used. The algorithm is easy: walk through the
- drive queue and see whether the number matches any of the
- existing drives.
- *)
- var
- currentElement: DrvQElPtr;
- begin
- DriveExists := false;
- currentElement := DrvQElPtr(GetDrvQHdr^.qHead);
- while currentElement <> nil do begin
- if currentElement^.dQDrive = driveNum then begin
- DriveExists := true;
- leave;
- end; (* if *)
- currentElement := DrvQElPtr(currentElement^.qLink);
- end; (* while *)
- end; (* DriveExists *)
-
- function FindFreeDriveNumber : integer;
- (* This routine finds a free drive number that we can use.
- The algorithm is simple: start at 5 and keep incrementing
- the number until we find one that isn't used.
- This routine makes the implicit assumption that the drive
- queue can never be full, ie that candidateDriveNumber will
- never wrap beyond 32767. While there's no technical
- reason that this can't happen, in practical terms
- it's *extremely* unlikely.
- *)
- var
- candidateDriveNumber : integer;
- begin
- candidateDriveNumber := 5; (* drive numbers 1-4 are reserved *)
- while DriveExists(candidateDriveNumber) do begin
- candidateDriveNumber := candidateDriveNumber + 1;
- end; (* while *)
- FindFreeDriveNumber := candidateDriveNumber;
- end; (* FindFreeDriveNumber *)
-
- function DriveToDriveRecord (driverRefNum : integer; driveNum: integer; var disk : DiskImageRecordPtr): OSErr;
- (* This routine finds the DiskImageRecord associated with a particular
- drive number. It simply walks the drive queue looking for
- the particular drive number and returns the queue element
- associated with that drive number. Remember that DiskImageRecord
- are record extensions of disk queue elements.
- There are two subtleties here:
-
- 1. We have to subtract 4 from the address of the disk queue element
- to get the DiskImageRecordPtr because of the flags that lurk
- in front of the disk queue element.
-
- 2. We only match a drive if the driver refnum matches
- along with the drive number. This guards someone passing
- us a valid drive that isn't for us. Of course, if someone
- has installed a drive with the same drive number as one
- of ours, the system is going to be very sick very quickly.
- *)
- var
- currentElement: DrvQElPtr;
- begin
- DriveToDriveRecord := nsDrvErr;
- currentElement := DrvQElPtr(GetDrvQHdr^.qHead);
- while currentElement <> nil do begin
- if (currentElement^.dQDrive = driveNum) & (currentElement^.dQRefNum = driverRefNum) then begin
- disk := DiskImageRecordPtr(longint(currentElement) - 4);
- DriveToDriveRecord := noErr;
- leave;
- end; (* if *)
- currentElement := DrvQElPtr(currentElement^.qLink);
- end; (* while *)
- end; (* DriveToDriveRecord *)
-
- function VolumeMountedOnDrive(driveNum : integer) : Boolean;
- (* This function returns true if there is a volume in the ejected state that
- was ejected from the given driven. It does this by walking the system
- VCB list. See IM: Files, p2-80 for a description of the meanings of
- vcbDrvNum and vcbDRefNum.
- *)
- var
- currentVolume : VCBPtr;
- begin
- VolumeMountedOnDrive := false;
- currentVolume := VCBPtr(GetVCBQHdr^.qHead);
- while currentVolume <> nil do begin
- if (currentVolume^.vcbDrvNum = 0) & (currentVolume^.vcbDRefNum = driveNum) then begin
- VolumeMountedOnDrive := true;
- leave;
- end; (* if *)
- currentVolume := VCBPtr(currentVolume^.qLink);
- end; (* while *)
- end; (* VolumeMountedOnDrive *)
-
- function CountInstalledDrives(driverRefNum : integer) : integer;
- (* This routine counts the number of drives that are currently
- installed that are being controlled by the given driver.
- *)
- var
- result : integer;
- currentElement: DrvQElPtr;
- begin
- result := 0;
- currentElement := DrvQElPtr(GetDrvQHdr^.qHead);
- while currentElement <> nil do begin
- if currentElement^.dQRefNum = driverRefNum then begin
- result := result + 1;
- end; (* if *)
- currentElement := DrvQElPtr(currentElement^.qLink);
- end; (* while *)
- CountInstalledDrives := result;
- end; (* CountInstalledDrives *)
-
- (* ***** Core Operations ***** *)
-
- function MountImage(pb: ParmBlkPtr; dctl: DCtlPtr): OSErr;
- (* This routine is called in response to a kMountImageControlCode call.
- pb point to a MountParamBlockPtr, which gives us the FSSpec of
- the file to mount.
- *)
- var
- err: OSErr;
- disk : DiskImageRecordPtr;
- junk: OSErr;
- driveNum: integer;
- begin
- (* Create the core DiskImageRecord and open the AFP fork to the file. *)
- err := CreateDiskImage(MountParamBlockPtr(pb)^.csParamFileToMount^, disk);
-
- (* Prepare the rest of the fields in the DiskImageRecord. *)
- if err = noErr then begin
-
- (* First the four flag bytes. *)
- disk^.driveFlags := signedByte($80); (* bit 7 set => read only drive *)
- disk^.diskInPlace := kDiskJustInserted;
- disk^.driveInstalled := 1;
- disk^.numberOfSides := -1;
-
- (* Now the drive queue element. *)
- driveNum := FindFreeDriveNumber;
- disk^.driveQElement.qType := drvQType;
- disk^.driveQElement.dQDrive := driveNum;
- disk^.driveQElement.dQRefNum := pb^.ioCRefNum;
- disk^.driveQElement.dQFSID := 0;
- disk^.driveQElement.dQDrvSz := disk^.diskSize div 512;
- disk^.driveQElement.dQDrvSz2 := 0;
-
- (* Most of the rest of the fields are setup by CreateDiskImage. The only
- other thing we need is to save away our DCE near the XPPParamBlock,
- so that the completion routine can get at it.
- *)
- disk^.driverDCE := dctl;
-
- (* Add the drive into the drive queue and post a suitable disk inserted event. *)
- AddDrive(dctl^.dCtlRefNum, driveNum, @disk^.driveQElement);
- junk := PostEvent(diskEvt, driveNum);
- end; (* if *)
-
- MountImage := err;
- end; (* MountImage *)
-
- procedure DoAccRun(pb: ParmBlkPtr; dctl: DCtlPtr);
- (* DoAccRun is called in response to an accRun control call. The call
- is guaranteed to be operating at SystemTask time, which means we
- can make synchronous calls and use the Memory Manager.
- The routine walks the drive queue looking for drives that belong
- to us that have been ejected and takes the appropriate action.
- Note the use of SysError(dsIOCoreErr) in this routine. This
- error should never occur because it implies that the drive
- queue element we just found by walking the drive queue is
- no longer *in* the drive queue. If that happens, someone
- is pulling our drive queue elements out at interrupt time,
- and, as far as we're concerned, the system is hosed.
- I spent a lot of time pondering whether to put this error
- in or just to ignore it. In general, you should only call
- SystemError when you have reason to believe that taking
- down the system is the *safest* thing to do at that point,
- because the system is in danger of corrupting user data.
- I think this situation fits.
- Note the description of dsIOCoreErr = 14 in "IM: Operating
- System Utils", p2-9.
- *)
- var
- err : OSErr;
- junk : OSErr;
- currentElement: DrvQElPtr;
- disk : DiskImageRecordPtr;
- begin
- (* First clear the dNeedTime flag for our driver. The order in which we do
- this is important. We need to do this before we start walking the
- drive queue, because UnMountImage sets the flag after it marks
- the drive as kDiskNotPresent. An UnMountImage call can happen
- at any time during this routine, but the routine will still
- do the right thing. If it happens before we clear the flag,
- we'll clear the flag and see the kDiskNotPresent drive
- in the drive queue. If it happens after we clear the flag,
- the flag will still be set when we leave this routine, so we'll
- get another accRun call.
- Also note that we use non-atomic operation to change the flag.
- This works because we're merely setting or clearing it,
- and don't care about it's previous value.
- *)
-
- {$setc BrokenBuggyCodeWarrior:=true}
- {$ifc BrokenBuggyCodeWarrior}
- dctl^.dCtlFlags := band(dctl^.dCtlFlags, $DFFF);
- {$elsec}
- (* BNOT seems to be broken under CW11. *)
- dctl^.dCtlFlags := band(dctl^.dCtlFlags, bnot(dNeedTimeMask));
- {$endc}
-
- (* Now walk the drive queue looking for drives that belong to us
- that are marked as kDiskNotPresent.
- *)
- currentElement := DrvQElPtr(GetDrvQHdr^.qHead);
- while currentElement <> nil do begin
- if currentElement^.dQRefNum = pb^.ioRefNum then begin
- disk := DiskImageRecordPtr(longint(currentElement) - 4);
- if disk^.diskInPlace = kDiskNotPresent then begin
-
- (* We've found such a drive. If the system still has a VCB for
- the volume that was recently ejected, then we just post
- a disk inserted event to reinsert the drive. This
- happen when the user hits "Eject" in the Finder.
- If there is not VCB for the drive, we remove the drive
- from the drive queue and dispose it. This is the standard
- unmount case, ie the user hits "Put Away" in the Finder".
- When we do this, we reset currentElement back to the head of
- the list, otherwise the "follow next link" code at the end of
- while loop would send us off into never-never land.
- Note that this distinction is only necessary because we've
- marked our drive as "ejectable", which is only necessary
- because we don't want VM paging off it. *sigh* I've
- already asked engineering for a better way of telling VM
- not to page off your drive in the future.
- *)
-
- if VolumeMountedOnDrive(disk^.driveQElement.dQDrive) then begin
- junk := PostEvent(diskEvt, disk^.driveQElement.dQDrive);
- end else begin
- if DeQueue(@disk^.driveQElement, GetDrvQHdr) = noErr then begin
- currentElement := DrvQElPtr(GetDrvQHdr^.qHead);
- end else begin
- SysError(dsIOCoreErr); (* See comment at start of routine. *)
- end; (* if *)
- err := DestroyDiskImage(disk);
- end; (* if *)
-
- end; (* if *)
- end; (* if *)
- currentElement := DrvQElPtr(currentElement^.qLink);
- end; (* while *)
- end; (* DoAccRun *)
-
- function UnMountImage(pb: ParmBlkPtr; dctl: DCtlPtr): OSErr;
- (* UnMountImage is called in response to a kEjectDiskControlCode call.
- This call may be issued synchronously or asynchronously, so
- we can't make synchronous calls or use the Memory Manager. For
- this reason, we simply mark the drive as ejected and defer the
- actual destruction of the drive queue element until DoAccRun time.
- *)
- var
- err: OSErr;
- disk : DiskImageRecordPtr;
- begin
- err := DriveToDriveRecord(pb^.ioRefNum, pb^.ioVRefNum, disk);
- if err = noErr then begin
- (* Simply mark the disk as no longer present, we'll clean it up in the DoAccRun routine. *)
- disk^.diskInPlace := kDiskNotPresent;
-
- (* Now set dNeedTime so that the system will start giving us accRun calls
- as soon as possible.
- *)
- dctl^.dCtlDelay := 1;
- dctl^.dCtlFlags := bor(dctl^.dCtlFlags, dNeedTimeMask);
- end; (* if *)
- UnMountImage := err;
- end; (* UnMountImage *)
-
- (* ***** Standard Driver Entry Points ***** *)
-
- function DRVROpen(pb: ParmBlkPtr; dctl: DCtlPtr): OSErr;
- (* Respond to the standard Open call. Most drivers do a lot
- more work here, such as walking a partition table and
- creates drives for all the partitions in the table. We
- don't need to do this because we create drives in
- response to the kMountImageControlCode call.
- *)
- begin
- {$unused pb}
- {$unused dctl}
- (* DebugStr('DRVROpen'); *)
-
- (* Remember A4 for use in our callbacks, specifically PascalCompletion. *)
- RememberA4;
-
- DRVROpen := InitDiskImageCore;
- end; (* DRVROpen *)
-
- function DRVRClose(pb: ParmBlkPtr; dctl: DCtlPtr): OSErr;
- (* Respond to the standard Close call. We only allow closing if
- we have no installed drives at the moment.
- *)
- var
- err: OSErr;
- begin
- {$unused dctl}
- if CountInstalledDrives(pb^.ioRefNum) = 0 then begin
- err := noErr;
- end else begin
- err := closErr;
- end; (* if *)
- DRVRClose := err;
- end; (* DRVRClose *)
-
- function DRVRPrime(pb: ParmBlkPtr; dctl: DCtlPtr): OSErr;
- (* Respond to the standard Prime call, ie Read/Write. The core
- read functionality (this sample makes all drives read-only)
- is performed by the ReadDiskImage routine.
- *)
- var
- err: OSErr;
- offset: longint;
- disk : DiskImageRecordPtr;
- begin
- (* First find the disk we're operating on. *)
- err := DriveToDriveRecord(pb^.ioRefNum, pb^.ioVRefNum, disk);
-
- if err = noErr then begin
-
- (* Now check the parameters to make sure everything is in range. *)
- offset := dctl^.dCtlPosition;
- if (offset < 0) or (pb^.ioReqCount < 0) or (offset + pb^.ioReqCount > disk^.diskSize) then begin
- pb^.ioActCount := 0;
- err := paramErr;
- end else begin
-
- (* Everything is good, lets do the read/write call. *)
- err := noErr;
- pb^.ioActCount := 0;
- if odd(pb^.ioTrap) then begin
- (* _Write *)
- err := wPrErr;
- end else begin
- (* _Read *)
- disk^.diskInPlace := kDiskHasBeenRead;
- err := ReadDiskImage(disk, pb^.ioPosOffset, pb^.ioReqCount, pb^.ioBuffer, @GenericCompletion);
- end; (* if *)
-
- (* Now adjust ioActCount and dCtlPosition. This only happens if
- ReadDiskImage returns noErr. However, because we called it
- asynchronously, ReadDiskImage will almost always return
- ioInProgress (ie 1). I left this code in so that I can
- switch the driver back to synchronous operations easily.
- The real setting of these values is done in the PascalCompletion
- routine.
- *)
- if err = noErr then begin
- pb^.ioActCount := pb^.ioReqCount;
- dctl^.dCtlPosition := dctl^.dCtlPosition + pb^.ioActCount;
- end; (* if *)
- end; (* if *)
- end; (* if *)
- DRVRPrime := err;
- end; (* DRVRPrime *)
-
- function DRVRControl(pb: ParmBlkPtr; dctl: DCtlPtr): OSErr;
- (* Respond to the standard Control calls. *)
- var
- err: OSErr;
- begin
- case pb^.csCode of
-
- (* Standard block device control calls. *)
-
- kKillIOControlCode: (* It's just too hard to implement KillIO in this sample. *)
- err := -1; (* Fortunately it's legal for me to just return an error. *)
-
- kVerifyDiskControlCode:
- err := noErr;
-
- kFormatDiskControlCode:
- err := noErr;
-
- kEjectDiskControlCode:
- err := UnMountImage(pb, dctl);
-
- kSetTagBufferControlCode:
- if pb^.ioMisc = nil then begin
- (* We're setting no tag buffer, succeed the call. *)
- err := noErr;
- end else begin
- (* We're setting a tag buffer, fail the call. *)
- err := -1;
- end; (* if *)
-
- kTrackCacheControlCode:
- err := -1;
-
- kPhysicalIconControlCode:
- begin
- pb^.ioMisc := @gPhysicalIconInfo;
- err := noErr;
- end;
-
- kMediaIconControlCode:
- begin
- pb^.ioMisc := @gMediaIcon;
- err := noErr;
- end;
-
- kDriveInfoControlCode:
- begin
- (* See Technote DV 17 for the format of these longint. *)
- pb^.ioMisc := Ptr($00000404);
- err := noErr;
- end;
-
- accRun:
- DoAccRun(pb, dctl);
-
- kTrackDumpControlCode:
- err := -1;
-
- kDriverConfigureCode:
- (* No driver configure calls are currently defined, but the csCode is reserved
- and drivers should not implement it.
- *)
- err := controlErr;
-
- (* Private control codes *)
-
- kMountImageControlCode:
- err := MountImage(pb, dctl);
-
- kSecondaryInitControlCode:
- begin
- (* See comment in AsyncDriverCommon.p *)
- gDriverVersion := SecondaryInitParamBlockPtr(pb)^.csParamVersion;
- gMediaIcon.icon := SecondaryInitParamBlockPtr(pb)^.csParamMediaIcon;
- gMediaIcon.description := SecondaryInitParamBlockPtr(pb)^.csParamMediaDescription;
- gPhysicalIconInfo.icon := SecondaryInitParamBlockPtr(pb)^.csParamPhysicalIcon;
- gPhysicalIconInfo.description := SecondaryInitParamBlockPtr(pb)^.csParamPhysicalDescription;
-
- (* It's also traditional to put the driver version into the
- driver's queue flags.
- *)
- dctl^.dCtlQHdr.qFlags := gDriverVersion.majorRev;
-
- err := noErr;
- end;
-
- otherwise
- (* It is important that you return controlErr to any control calls that
- you don't understand.
- *)
- err := controlErr;
- end; (* case *)
-
- DRVRControl := err;
- end; (* DRVRControl *)
-
- function DRVRStatus(pb: ParmBlkPtr; dctl: DCtlPtr): OSErr;
- (* Respond to the standard Status calls. *)
- var
- err: OSErr;
- disk : DiskImageRecordPtr;
- responsePtr : Ptr;
- begin
- {$unused dctl}
- case pb^.csCode of
-
- kReturnFormatListStatusCode:
- begin
- err := DriveToDriveRecord(pb^.ioRefNum, pb^.ioVRefNum, disk);
- if err = noErr then begin
- if ExtendedParamBlockPtr(pb)^.formatCount > 0 then begin
- ExtendedParamBlockPtr(pb)^.formatCount := 1;
- ExtendedParamBlockPtr(pb)^.formatPoint^.capacityInBlocks := disk^.diskSize div 512;
- ExtendedParamBlockPtr(pb)^.formatPoint^.flagsAndOtherInfo := 0;
- err := noErr;
- end else begin
- err := paramErr;
- end; (* if *)
- end; (* if *)
- end;
-
- kDriveStatusStatusCode:
- begin
- err := DriveToDriveRecord(pb^.ioRefNum, pb^.ioVRefNum, disk);
- if err = noErr then begin
- ExtendedParamBlockPtr(pb)^.statusCurrentTrack := 0;
- ExtendedParamBlockPtr(pb)^.statusFlags := disk^.driveFlags;
- ExtendedParamBlockPtr(pb)^.statusDiskInPlace := disk^.diskInPlace;
- ExtendedParamBlockPtr(pb)^.statusDriveInstalled := disk^.driveInstalled;
- ExtendedParamBlockPtr(pb)^.statusNumberOfSides := disk^.numberOfSides;
- ExtendedParamBlockPtr(pb)^.statusDriveQElement := disk^.driveQElement;
- (* The next two lines are required to conform to the interface described
- in Technote DV 17. dQDrvSz maps to two bytes ("two sided" and
- "new interface"), both of which should be -1. dQDrvSz2 maps
- to "soft error count".
- *)
- ExtendedParamBlockPtr(pb)^.statusDriveQElement.dQDrvSz := -1;
- ExtendedParamBlockPtr(pb)^.statusDriveQElement.dQDrvSz2 := 0;
- err := noErr;
- end; (* if *)
- end;
-
- kDriverGestaltCode:
- (* For more information about DriverGestalt, see
- "Designing PCI Cards and Drivers for Power Macintosh Computers", p106.
- *)
- begin
- err := noErr;
- case UInt32(DriverGestaltParamPtr(pb)^.driverGestaltSelector) of
- UInt32(kdgVersion):
- DriverGestaltParamPtr(pb)^.driverGestaltResponse := UInt32(gDriverVersion);
- UInt32(kdgDeviceType):
- DriverGestaltParamPtr(pb)^.driverGestaltResponse := UInt32(kdgFileType);
- UInt32(kdgSync):
- DriverGestaltParamPtr(pb)^.driverGestaltResponse := 0; (* Only high byte is meaningful. *)
- UInt32(kdgPurge):
- begin
- responsePtr := @pb^.ioBuffer;
- DriverGestaltPurgeResponsePtr( responsePtr )^.purgePermission := kmOkCloseOkPurge;
- DriverGestaltPurgeResponsePtr( responsePtr )^.purgeDriverPointer := dctl^.dCtlDriver;
- end;
- UInt32(kdgEject):
- DriverGestaltParamPtr(pb)^.driverGestaltResponse := 0; (* We always want ejects. *)
- (* We don't support the following DriveGestalt codes:
- kdgInterface:
- { The underlying interface that the driver is using (if any) }
- kdgBoot:
- { value to place in PRAM for this drive (long) }
- kdgWide:
- { True if driver supports ioWPosOffset }
- kdgSupportsSwitching:
- { True if driver supports power switching }
- kdgMin3VPower:
- { Minimum 3.3V power consumption in microWatts }
- kdgMin5VPower:
- { Minimum 5V power consumption in microWatts }
- kdgMax3VPower:
- { Maximum 3.3V power consumption in microWatts }
- kdgMax5VPower:
- { Maximum 5V power consumption in microWatts }
- kdgInHighPower:
- { True if device is currently in high power mode }
- kdgSupportsPowerCtl:
- { True if driver supports following five calls }
- kdgAPI:
- { API support for PC Exchange }
- kdgFlush:
- { Determine if disk driver supports flush and if it needs a flush }
- *)
- otherwise
- err := statusErr;
- end; (* case *)
- end;
-
- otherwise
- (* It is important that you return statusErr to any status calls that
- you don't understand.
- *)
- err := statusErr;
- end; (* case *)
- DRVRStatus := err;
- end; (* DRVRStatus *)
-
- end. (* AysncDriverSample *)